
# Simulator states
simulation_deadlock = -2
end_of_time = -1
end_of_simulation = 0
continue_simulation = 1

# Thread states
thread_running = 0
thread_waiting_time = 1
thread_waiting_event = 2
thread_finished = 3
thread_state_names = {
0:"thread_running",
1:"thread_waiting_time",
2:"thread_waiting_event",
3:"thread_finished"
}

wait_time = 0
wait_event = 1

class Simulator:
	def __init__(self):
		self.components = { }
		self.threads = [ ]
		self.times_to_wait = { }
		self.thread_names = { }
		self.events_to_wait = { }
		self.thread_states = { }
		self.thread_components = { }
		self.time=0
		self.thread_counter = 0
		self.verbose = 0
	
	# Rezume all threads that are scheduled to run
	def evaluate(self):
		if self.verbose: print "==============evaluate(time="+str(self.time)+"):"
		if self.verbose: print "==Old thread states:"
		if self.verbose: print self.thread_states
		if self.verbose: print "==Old times to wait:"
		if self.verbose: print self.times_to_wait
		if self.verbose: print "==="
		for t in self.threads:
			# If thread was killed close it
			if self.thread_states[t]==thread_finished:
				self.remove_thread(t)
				continue
			elif self.thread_states[t]==thread_running:
				try:
					state=t.next()
				except StopIteration:
					if self.verbose: print "==Thread stopped:"+self.thread_names[t]
					self.remove_thread(t)
					state=None,None
				# Is thread is waiting for time or event update the wait dictionaries
				if state[0]==wait_time:
					self.thread_states[t]=thread_waiting_time
					self.times_to_wait[t] = state[1]
				elif state[0]==wait_event:
					self.thread_states[t]=thread_waiting_event
					self.events_to_wait[t] = state[1]
		if thread_finished in self.thread_states.values():
			self.threads = [ it for it in self.threads if self.thread_states[it]!=thread_finished ]
		if self.verbose: print "==="
		if self.verbose: print "==New thread states:"
		if self.verbose: print self.thread_states
		if self.verbose: print "==New times to wait:"
		if self.verbose: print self.times_to_wait
	
	# Update simulation state
	def update(self,time_limit=-1):
		if self.verbose: print "==============update:"
		# Check deadlock condition
		deadlock=True
		allfinished=True
		if self.verbose: print "==Old thread states:"
		if self.verbose: print self.thread_states
		if self.verbose: print "==Old times to wait:"
		if self.verbose: print self.times_to_wait
		if self.verbose: print "==="
		for t,s in self.thread_states.iteritems():
			if(s!=thread_waiting_event):
				deadlock=False
			if(s!=thread_finished):
				allfinished=False
		if(deadlock):
			return simulation_deadlock
		if(allfinished):
			return end_of_simulation
		# Update threads waiting for time
		min_time=min(self.times_to_wait.values())
		for t,wt in self.times_to_wait.iteritems():
			if(wt==min_time):
				self.thread_states[t]=thread_running
			else:
				if(self.time+min_time<time_limit or time_limit==-1):
					self.times_to_wait[t]-=min_time
				else:
					self.times_to_wait[t]-=(self.time+min_time)-time_limit
		# Update threade waiting for events
		for t,e in self.events_to_wait.iteritems():
			if(e.state==1):
				self.thread_states[t]=thread_running
				e.consume()
		
		if self.verbose: print "==="
		if self.verbose: print "==New thread states:"
		if self.verbose: print self.thread_states
		if self.verbose: print "==New times to wait:"
		if self.verbose: print self.times_to_wait
		
		# Update time
		if(time_limit==-1):
			if self.verbose: print "==min_time=%d" %min_time
			self.time += min_time
			if self.verbose: print "==global time=%d" %self.time
			return continue_simulation
		elif(self.time+min_time>=time_limit):
			self.time = time_limit
			return end_of_time
	
	# Recursively call the run method of all the sub-components of root
	def run(self,root):
		root.run()
		if root.has_childred():
			for c in root.get_childred():
				self.run(c)

	# Find all the root components and recursively call the run methods of their children
	def initialize(self):
		roots = filter(lambda x:self.components[x]==None,self.components.keys())
		for r in roots:
			self.run(root)

	def simulate_comp(self,timelimit=-1):
		self.initialize()
		while(True):
			self.evaluate()
			eos=self.update(timelimit)
			if(eos!=continue_simulation): break
		return eos

	def simulate(self,timelimit=-1):
		while(True):
			self.evaluate()
			eos=self.update(timelimit)
			if(eos!=continue_simulation): break
		return eos

	def register_component_thread(self,comp,t):
		self.threads.append(t)
		self.thread_components[t] = comp
		self.thread_states[t] = thread_running
		self.thread_names[t]=t.__name__+'_'+str(self.thread_counter)
		self.thread_counter+=1
		return t

	def register_thread(self,t):
		self.threads.append(t)
		self.thread_states[t] = thread_running
		self.thread_names[t]=t.__name__+'_'+str(self.thread_counter)
		self.thread_counter+=1
	
	def register_component(self,comp):
		self.components[comp]=comp.parent
	
	def kill_thread_by_handle(self,t):
		thread_states[t]=thread_finished
	
	def kill_thread_by_name(self,tn):
		if tn in thread_names.values():
			th = filter(lambda x:self.thread_names[x]==tn,self.thread_names.keys())
			kill_thread_by_handle(th)
	
	def remove_thread(self,t):
		self.thread_states[t]=thread_finished
		try: del self.times_to_wait[t]
		except: pass				
		try: del self.events_to_wait[t]
		except: pass


